package engine

import (
    "log"
    "spider/concurrent2/parser"
    "spider/concurrent2/scheduler"
)

// 并发多任务引擎
type ConcurrentEngine struct {
    // 调度器接口
    Scheduler   scheduler.Scheduler
    WorkerCount int
}

func (ConcurrentQueueEngine *ConcurrentEngine) Run(seeds ...parser.Request) {
    // 创建输出channel
    out := make(chan parser.ParseResult)
    
    ConcurrentQueueEngine.Scheduler.Run()
    
    // 创建worker
    ConcurrentQueueEngine.initWorker(out, ConcurrentQueueEngine.Scheduler)
    
    // 提交request给带调度器
    for _, request := range seeds {
        ConcurrentQueueEngine.Scheduler.Submit(request)
    }
    
    counter := 0
    // 处理worker执行结果
    for {
        // 注意此处每获取一次结果(result := <- out)，需要将结果打印出来，并且将新的request分发出去
        // 当无空闲worker时，会无法将新的request分发出去,会产生worker之间循环等待
        // 因此可以使用goroutine分发任务，防止循环等待(ConcurrentEngine.Scheduler.Submit(request))
        // 具体看慕课谷歌go视频25-2 15分钟后半段
        result := <-out
        for _, item := range result.ReponseSlice {
            log.Printf("Got item #%d: %v\n", counter, item)
            counter++
        }
        
        for _, request := range result.Requests {
            ConcurrentQueueEngine.Scheduler.Submit(request)
        }
    }
}

// 初始化worker
func (ConcurrentQueueEngine *ConcurrentEngine) initWorker(out chan parser.ParseResult, scheduler scheduler.Scheduler) {
    for i := 0; i < ConcurrentQueueEngine.WorkerCount; i++ {
        createWorker(scheduler.WorkerRequestChan(), out, scheduler)
    }
}

// 创建worker
func createWorker(in chan parser.Request, out chan parser.ParseResult, scheduler scheduler.Scheduler) {
    go func() {
        for {
            // 告诉调度器，当前worker进入ready状态，然后等待处理任务
            scheduler.WorkerReady(in)
            
            // request channel中接收到任务，取出来进行处理
            request := <-in
            
            result, err := worker(request)
            if err != nil {
                continue
            }
            
            // 将处理结果放到out channel中
            out <- result
        }
    }()
}
